/*
 * VarType.java
 *
 * Created on July 31, 2007, 7:54 PM
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package tal.drivechain.variable.types;

import tal.drivechain.chain.ChainNode;
import tal.drivechain.matchers.ValueMatcher;
import tal.drivechain.matchers.VarMatcher;
import tal.drivechain.variable.Variable;

/**
 * A DataType class that handles references to other variables as values.
 * Name: "var"
 * Value object: {@link VarReference}
 * Type code: 9
 * Syntax: var:<other variable definition> (var:int:6) or var:(ref)<external variable name> (var:(ref)sys.now)
 * Can cast from any valid DataType to a var variable that contains the cast value.
 * The var data type is useful for 3 things:
 *  1. As a reference to another external variable. Useful if you want the var variable to have
 *     the same value as the referenced variable even when the reference changes it value after storing
 *     the var variable.
 *  2. As a way for copying a variable into an other one. For example if you want to send out the
 *     current time you can use -p Send:init=newnow:var:(ref)sys.now. This command will send when the
 *     chain is opened the current time variable. 
 *     newnow is sent out from the Send module the reference will be sent as a fixed time variable. That means
 *     that if sys.now equals time:5ms when init is sent then newnow will be time:5ms after sending it
 *     instead of var:(ref)sys.now.
 *  3. To create arrays of variables of different data types. This is very useful in case complex data structures
 *     are needed. For example the WiiRemoteIn module can receive var arrays for different commands. To lit the
 *     Wii remote's LEDs you send a var array with a string element and up to 4 pairs of int and boolean elemenet
 *     representing the state of each of the 4 leds of the remote.
 *     
 * @author Tal Eisenberg
 */
public final class VarType extends DataType {
    public VarType() {
        super("var", Object.class, 9);
    }

    public Object parseValue(String value) throws IllegalArgumentException {
        try {
            return Variable.parseVariable("var", null, value, false).getInternalValue();
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException("Illegal var value: " + e.getMessage() + " (" + value + ").");
        }
    }

    @Override
    public Variable evaluateVar(Variable var, ChainNode context) {
        Object val = var.getInternalValue();
        DataType t = DataType.findType(val);
        if (t==null) throw new IllegalArgumentException("Can't evaluate var type variable: " + var.toString() + ": Unknown value object " + val.getClass().getName());
        else if (t.matches(this)) {
            return var;
        } else return new Variable(var.getName(), t, val, true).evaluate(context);
    }
    
    @Override
    public Object cast(Object value) throws IllegalArgumentException {
        return value;
    }
    
    @Override
    public Object extractValue(Object value, ChainNode context) {
        return value;
        /*
        if (value instanceof List || value instanceof HashMap) {
            return value;
        } else {
            DataType t = DataType.findType(value);
            if (t==null) throw new IllegalArgumentException("Can't extract var type value: " + value + ": Unknown value object " + value.getClass().getName());
            return new Variable("temp", t, value, true).getValue(context);
        }*/
    }
    
    @Override
    public String toString(Object value, ChainNode context) {
        if (value==null) return "";
        else return value.toString();
    }

    @Override
    public ValueMatcher createValueMatcher() throws IllegalArgumentException {
        return new VarMatcher();
    }
}
